#include "vd_fb.h"

#define X_RES_VIR 640       //虚拟的水平大小
#define Y_RES_VIR 480       //虚拟的垂直大小
#define X_RES 640           //实际的水平大小
#define Y_RES 480           //实际的垂直大小

#ifdef LCD_GRAY_16        //LCD彩色和灰级度位16灰级度
#define LCD_DEPTH 4     //4 //每一个像素占4位
#define LCD_BUF_SIZE ((X_RES_VIR*Y_RES_VIR)>>1)   //320*240/2
#else
#define LCD_DEPTH 8     //每一个像素占8位
#define LCD_BUF_SIZE ((X_RES_VIR*Y_RES_VIR))   //320*240大小
#endif


static struct fb_var_screeninfo vd_fb_var_default __initdata = {
    .xres =		X_RES,
    .yres =		Y_RES,
    .xres_virtual =	X_RES_VIR,
    .yres_virtual =	Y_RES_VIR,

    .bits_per_pixel = 32,
    .red =		{ 16, 8, 0 },
    .green =	{ 8, 8, 0 },
    .blue =		{ 0, 8, 0 },
    .transp=    { 24, 8, 0 },

    .activate =	FB_ACTIVATE_NOW,
    .height =	-1,
    .width =	-1,
    .pixclock =	20000,
    .left_margin =	64,
    .right_margin =	64,
    .upper_margin =	32,
    .lower_margin =	32,
    .hsync_len =	64,
    .vsync_len =	2,
    .vmode =	FB_VMODE_NONINTERLACED,
};

static struct fb_fix_screeninfo vd_fb_fix_default __initdata = {
	.id =		DEVICE_NAME,
	.type =		FB_TYPE_PACKED_PIXELS,
};

/* The fb_ops structure */
static struct fb_ops vd_fb_fops = {
    .owner = THIS_MODULE,
    .fb_check_var = vd_fb_check_var,
    .fb_set_par = vd_fb_set_par,
    .fb_setcolreg = vd_fb_setcolreg,
    .fb_blank = vd_fb_blank,
    .fb_fillrect = vd_fb_fillrect,
    .fb_copyarea = vd_fb_copyarea,
    .fb_imageblit = vd_fb_imageblit,
    .fb_rotate = vd_fb_rotate,
    .fb_ioctl = vd_fb_ioctl,
};

int vd_fb_drv_probe(struct platform_device *pdev)
{
    struct fb_info *fbinfo;
    struct fb_fix_screeninfo *fix;
    char *fbuff = NULL;
    int err = 0;

    fbuff = kmalloc(LCD_BUF_SIZE, GFP_KERNEL);

    if (fbuff == NULL)
    {
        printk("vd fb buff mallock failed\n");
        goto out;
    }

    fbinfo = framebuffer_alloc(0, &pdev->dev);

    if (!fbinfo) {
        printk("vd fb info alloc failed\n");
        goto free_fbuff;
    }

    dev_set_drvdata(&pdev->dev, fbinfo);

    fbinfo->screen_base = (char __iomem *)fbuff;

    fix = &fbinfo->fix;

    *fix = vd_fb_fix_default;

    fix->smem_start = (unsigned long)fbuff;

    fix->smem_len = LCD_BUF_SIZE;

    fix->type = FB_TYPE_PACKED_PIXELS;

    fbinfo->pseudo_palette = fbinfo->par;
    fbinfo->par = NULL;

    fbinfo->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB | FBINFO_READS_FAST | FBINFO_HWACCEL_DISABLED;

    fbinfo->var = vd_fb_var_default;

    fbinfo->fbops = &vd_fb_fops;
    err = fb_alloc_cmap(&fbinfo->cmap, 256, 0);

    if (err < 0)
        goto free_fbuff;

    err = register_framebuffer(fbinfo);

    if (err)
        goto free_fbuff;

    return 0;

free_fbuff:
    kfree(fbuff);
    fbuff = NULL;

out:
    return err;
}

int vd_fb_drv_remove(struct platform_device *pdev)
{
    struct fb_info *fbinfo = platform_get_drvdata(pdev);
    struct fb_fix_screeninfo *fix = &fbinfo->fix;

    kfree((void*)fix->smem_start);

    unregister_framebuffer(fbinfo);

    fb_dealloc_cmap(&fbinfo->cmap);

    framebuffer_release(fbinfo);

    platform_set_drvdata(pdev, NULL);

    return 0;
}
